# -*- encoding:utf-8 -*-
# __author__ = 'chenyongbing'
# __poject__ = 'ops_platform'
# __datetime__ = '2022/8/4'
# __name__ = 'base.py'

from utils.kubernetes_helper import KubeClient
from utils.imagesync_helper import ImageSyncHelper
import os
import re
import datetime
from utils.acr_helper import AcrHelper
from utils.gitlab_helper import GL
from apps.cicd.cache import set_deployment_info_cache

from apps.cicd.models import WorkOrderStep, LobEnvSetting
from apps.resource.models import LobPerm, Service, Role
# Create your code here.


def get_kube_by_service_env(service, env):
    kube = None
    namespace = None
    project = service.project
    business = service.business
    service_setting = LobEnvSetting.objects.filter(env_id=env.id, service_id=service.id).first()
    if service_setting:
        kube = kube or service_setting.kube
        namespace = namespace or service_setting.namespace
    if not kube or not namespace:
        project_setting = LobEnvSetting.objects.filter(env_id=env.id, service_id__isnull=True, project_id=project.id).first()
        if project_setting:
            kube = kube or project_setting.kube
            namespace = namespace or project_setting.namespace
    if not kube or not namespace:
        business_setting = LobEnvSetting.objects.filter(env_id=env.id, service_id__isnull=True, project_id__isnull=True,
                                                        business_id=business.id).first()
        if business_setting:
            kube = kube or business_setting.kube
            namespace = namespace or business_setting.namespace
    return kube, namespace

def get_workorder_kube_and_namespace(workorder):
    service = workorder.service
    env = workorder.env
    data = {}
    kube, namespace = get_kube_by_service_env(service, env)
    if not kube or not namespace:
        return data
    data = {
        'kubeid': kube.id,
        'kubeconfig': kube.kubeconfig,
        'namespace': namespace
    }
    return data


def get_workorder_template(workorder):
    service_id = workorder.service_id
    env_id = workorder.env_id
    project = workorder.service.project
    business = workorder.service.business
    filterparams = [
        {'env_id': env_id, 'service_id': service_id},
        {'env_id': env_id, 'project_id': project.id, 'service_id__isnull': True},
        {'env_id': env_id, 'business_id': business.id, 'project_id__isnull': True, 'service_id__isnull': True}
    ]
    for filterparam in filterparams:
        lobsetting_obj = LobEnvSetting.objects.filter(**filterparam).first()
        if not lobsetting_obj: continue
        template = lobsetting_obj.template
        if template:
            return template.yaml



def get_workorder_namespace(workorder):
    service_id = workorder.service_id
    env_id = workorder.env_id
    project = workorder.service.project
    business = workorder.service.business
    filterparams = [
        {'env_id': env_id, 'service_id': service_id},
        {'env_id': env_id, 'project_id': project.id, 'service_id__isnull': True},
        {'env_id': env_id, 'business_id': business.id, 'project_id__isnull': True, 'service_id__isnull': True}
    ]
    for filterparam in filterparams:
        lobsetting_obj = LobEnvSetting.objects.filter(**filterparam).first()
        namespace = lobsetting_obj.namespace
        if namespace:
            return namespace

# for flow
def get_workorder_repo(workorder):
    service = workorder.service
    business = service.business
    lobenvsettingobj = LobEnvSetting.objects.filter(business_id=business.id, project_id=None, service_id=None,
                                                    env_id=workorder.env_id).first()
    if not lobenvsettingobj:
        return {}
    repo = lobenvsettingobj.repo
    return {
        'namespace': lobenvsettingobj.repo_namespace,
        'instance': repo.instance,
        'username': repo.username,
        'password': repo.password,
        'url': repo.url
    }


def workorder_deployment_ready_status(data):
    status = data.get('status', {})
    if data.get("generation", 1) == status.get("observed_generation") and \
            status.get("ready_replicas", 100) == status.get("replicas",99) and \
            status.get("unavailable_replicas", 99) is None:
        return True
    return False

#  通过service 、 env 获取相关配置
def get_repo_and_namespace_by_service(service, env):
    project = service.project
    business = service.business
    repo = None
    repo_namespace = None
    repo_namespace_src = None
    filterparams = [
        {'env_id': env.id, 'service_id': service.id},
        {'env_id': env.id, 'project_id': project.id, 'service_id__isnull': True},
        {'env_id': env.id, 'business_id': business.id, 'project_id__isnull': True, 'service_id__isnull': True}
    ]
    for filterparam in filterparams:
        lobsetting_obj = LobEnvSetting.objects.filter(**filterparam).first()
        if repo and repo_namespace:
            break
        if lobsetting_obj:
            repo = repo or lobsetting_obj.repo
            repo_namespace = repo_namespace or lobsetting_obj.repo_namespace
            repo_namespace_src = repo_namespace_src or lobsetting_obj.repo_namespace_src
    if not repo: return {}
    data = {'type': repo.type, 'repo_namespace': repo_namespace, 'repo_namespace_src': repo_namespace_src,
            'url': repo.url, 'username': repo.username, 'password': repo.password}
    if repo.type == 'aliyun':
        data['auth'] = {
            'access_key_id': repo.access_key_id,
            'access_key_secret': repo.access_key_secret,
            'instance_id': repo.instance
        }
    return data



def workorder_repo_and_namespace(workorder):
    service = workorder.service
    env = workorder.env
    data = get_repo_and_namespace_by_service(service, env)
    return data


def workorder_is_need_audit(workorder):
    audit = 1
    service_id = workorder.service_id
    service = Service.objects.get(id=service_id)
    lobsetting = LobEnvSetting.objects.filter(business_id=service.business.id, env_id=workorder.env_id, project_id__isnull=True, service_id__isnull=True).first()
    if lobsetting and lobsetting.auto_audit == 1:
    # if workorder.env.audit == 0:
        audit = 0
    return audit




def workorder_step_opuser(workorder, step, username):
    if re.search('auto', step.function):
        return 'auto'
    perm_role = step.perm_role
    if perm_role == None:
        return username
    if re.search('audit', step.function):
        audit = workorder_is_need_audit(workorder)
        if not audit:
            return 'auto'

    business = workorder.service.business
    roleids = []
    for role_code in perm_role.split(','):
        role = Role.objects.get(code=role_code)
        roleids.append(role.id)
    users = []
    for perm in LobPerm.objects.filter(business=business, roles__in=roleids):
        username = perm.user.username
        users.append(username)
    return ','.join(users)


def workorder_update_pod(workorder):
    service_name = workorder.service.name
    kube = get_workorder_kube_and_namespace(workorder)
    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)
    repoinfo = get_workorder_repo(workorder)
    if not repoinfo: return False
    image = "{repo_url}/{repo_namespace}/{repo_name}:{tag}".format(repo_url=repoinfo.get('url'),
                                                                   repo_namespace=repoinfo.get('namespace'),
                                                                   repo_name=service_name,
                                                                   tag=workorder.version)
    kubecli.deployment_update_image(name=service_name + '-deployment', namespace=namespace, image=image)
    return True



def workorder_restart_pod(workorder):
    service_name = workorder.service.name
    kube = get_workorder_kube_and_namespace(workorder)

    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)
    redeploy_timestamp = str(int(datetime.datetime.now().timestamp() * 1000))
    ret = kubecli.deployment_restart(namespace=namespace, name=service_name + '-deployment',
                                     redeploy_timestamp=redeploy_timestamp)
    return True


def workorder_pod_status(workorder):
    service_name = workorder.service.code
    env_name = workorder.env.code
    kube = get_workorder_kube_and_namespace(workorder)

    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)
    # ret = kubecli.check_deployment_rollout_status(namespace=namespace, name=service_name + '-deployment')
    ret = kubecli.get_deployment_status(namespace=namespace, name=service_name + '-deployment')
    set_deployment_info_cache(service_name, env_name, ret)
    status = workorder_deployment_ready_status(ret)
    return status


def workorder_get_image(workorder):
    service_name = workorder.service.name
    kube = get_workorder_kube_and_namespace(workorder)

    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)
    image = kubecli.get_deployment_image(namespace=namespace, name=service_name + '-deployment')
    return image


def get_repo_tags(repoinfo, repo_name, limit=3):
    data = []
    if repoinfo.get('type') == 'aliyun':
        data = get_acr_repo_tags(**repoinfo.get('auth', {}), repo_namespace=repoinfo.get('repo_namespace'),
                                 repo_name=repo_name, limit=limit)
    return data


def get_acr_repo_tags(instance_id=None, access_key_id=None, access_key_secret=None, repo_namespace=None, repo_name=None,
                      limit=3):
    acr = AcrHelper(access_key_id=access_key_id, access_key_secret=access_key_secret)
    data = acr.get_repo_tags(instance_id=instance_id, repo_namespace_name=repo_namespace, repo_name=repo_name,
                             page_size=limit)
    return data



def workorder_repo_tag_ready(workorder):
    service = workorder.service
    env = workorder.env
    version = workorder.version
    if not version: return False
    repoinfo = workorder_repo_and_namespace(workorder)
    repo_namespace_src = repoinfo.get('repo_namespace_src')
    repo_namespace = repoinfo.get('repo_namespace')
    if not repo_namespace_src or repo_namespace == repo_namespace_src:
        return True
    data = get_repo_tags(repoinfo, service.code, limit=20)
    tags = [d.get("Tag") for d in data]
    if version in tags:
        return True
    registry = repoinfo.get('url')
    ish = ImageSyncHelper(registry, repoinfo.get('username'), repoinfo.get('password'))
    image_src = registry + '/' + repo_namespace_src + '/' + service.code + ':' + version
    image_dest = registry + '/' + repo_namespace + '/' + service.code + ':' + version
    images = {image_src:image_dest}
    ret = ish.sync(images, workorder.id)
    return ret


def workorder_scale_pod(workorder):
    service_name = workorder.service.name
    replicas = workorder.replicas
    kube = get_workorder_kube_and_namespace(workorder)

    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)
    redeploy_timestamp = str(int(datetime.datetime.now().timestamp() * 1000))
    ret = kubecli.deployment_scale(namespace=namespace, name=service_name + '-deployment',
                                   replicas=replicas)
    return True


def workorder_rollback_pod(workorder):
    service_name = workorder.service.name
    kube = get_workorder_kube_and_namespace(workorder)
    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)
    image = workorder.cur_image
    kubecli.deployment_update_image(name=service_name + '-deployment', namespace=namespace, image=image)
    return True



def workorder_git_merge(workorder):
    service = workorder.service
    git_url = service.git_url
    git_master_branch = service.git_master_branch
    if not (git_url and git_master_branch):
        git_master_branch = service.project.git_master_branch
        git_url = service.project.git_url

    if not (git_url and git_master_branch):
        return True, "服务: {} 未配置相关git，跳过替换".format(service.code)
    project_name_with_namespace = re.search("://[^\/]+/(.*?).git", git_url).group(1)

    if re.search('_{}_'.format(git_master_branch), workorder.version):
        return True, "服务: {}, 版本 {} 包含分支 {} ，无需替换".format(service.code, workorder.version, git_master_branch)

    params = get_config_by_group(group='gitlab')
    gl = GL(**params)
    ret, msg = gl.replace_branch(project_name_with_namespace, version=workorder.version, target=git_master_branch)

    return ret, msg

def workorder_create_deployment(workorder):
    service_name = workorder.service.name
    kube = get_workorder_kube_and_namespace(workorder)
    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)

    kubecli.create_update_from_yaml(name=service_name + '-deployment', namespace=workorder.setting.namespace, yaml_text=workorder.yaml)
    return True



def workorder_update_deployment_resources(workorder):

    service = workorder.service
    deployment_name = service.name + '-deployment'
    capacity = workorder.capacity
    resources = {
                    'limits': {'cpu': str(capacity.cpu), 'memory': str(int(capacity.memory * 1000)) + 'Mi'},
                    'requests': {'cpu': str(capacity.cpu), 'memory': str(int(capacity.memory * 1000)) + 'Mi'}
    }
    memory = capacity.memory
    envs = []
    if service.cate == 'java':
        java_memory = str(int(memory * int(1000 * 0.8)))
        java_opts = "-Xms{memory}m -Xmx{memory}m".format(memory=java_memory)
        envs = [{'name': 'JAVA_OPTS', 'value': java_opts}]

    kube = get_workorder_kube_and_namespace(workorder)
    kubeid = str(kube.get('kubeid'))
    kubeconfig = kube.get('kubeconfig')
    namespace = kube.get('namespace')
    kubecli = KubeClient(kubeid, kubeconfig)

    kubecli.deployment_update_resources_and_env(name=deployment_name, namespace=namespace, resources=resources, envs=envs)
    return True